import json
import os
import xml.etree.ElementTree as ET
import pandas as pd
from collections import defaultdict, deque
import requests
from pm4py import convert_to_petri_net, convert_to_bpmn
from pm4py.objects.bpmn.exporter.variants.etree import get_xml_string
from pm4py.objects.bpmn.importer import importer as bpmn_importer

from pm4py.objects.bpmn.layout import layouter as bpmn_layouter
from datetime import datetime
import uuid

from promoai.general_utils import constants

import os

SAVE_PATH = os.path.join(os.getcwd(), "bpmn")
os.makedirs(SAVE_PATH, exist_ok=True)  # 如果目录不存在则创建，exist_ok=True 表示目录存在也不报错

# 命名空间定义
NS = {
    'bpmn': 'http://www.omg.org/spec/BPMN/20100524/MODEL'
}


def ensure_elements_named(xml_bytes, encoding=constants.DEFAULT_ENCODING):
    """
    确保 BPMN 文件中所有元素都有 name：
    - 若 name 为空或缺失：
        * exclusiveGateway → exclusiveGateway+id
        * parallelGateway → parallelGateway+id
        * 其他元素 → id
    - 返回修改后的 XML 字节
    """
    if isinstance(xml_bytes, bytes):
        root = ET.fromstring(xml_bytes)
    else:
        root = ET.fromstring(xml_bytes.encode(encoding))

    process = root.find('bpmn:process', NS)
    if process is None:
        return xml_bytes  # 没有 process，直接返回原始

    for elem in process:
        elem_id = elem.attrib.get('id')
        if not elem_id:
            continue

        name = (elem.attrib.get('name') or '').strip()
        if not name:
            tag = elem.tag.split('}')[-1]  # 去掉命名空间前缀
            if tag == "exclusiveGateway":
                elem.set('name', f"exclusiveGateway{elem_id}")
            elif tag == "parallelGateway":
                elem.set('name', f"parallelGateway{elem_id}")
            else:
                elem.set('name', elem_id)

    return ET.tostring(root, encoding=encoding)



def save_bpmn_and_parse(process_model_obj):
    try:
        # 第一步：生成 BPMN
        powl = process_model_obj.get_powl()
        pn, im, fm = convert_to_petri_net(powl)
        bpmn = convert_to_bpmn(pn, im, fm)

        # 第二步：导出 XML，并给所有未命名元素补齐 name=id
        bpmn_xml = get_xml_string(
            bpmn, parameters={"encoding": constants.DEFAULT_ENCODING}
        )
        bpmn_xml = ensure_elements_named(bpmn_xml, encoding=constants.DEFAULT_ENCODING)

        # ⚡ 关键：重新导入 BPMN，保证对象中的 name/id 与 XML 一致
        import tempfile
        with tempfile.NamedTemporaryFile(delete=False, suffix=".bpmn") as tmp:
            tmp.write(bpmn_xml)
            tmp_path = tmp.name

        bpmn = bpmn_importer.apply(tmp_path)

        # 保存文件
        file_name = f"generated_model_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:6]}.bpmn"
        bpmn_path = os.path.join(SAVE_PATH, file_name)
        with open(bpmn_path, "wb") as f:
            f.write(bpmn_xml)

        # 第三步：解析，返回未删除控制节点的sequence_json
        sequence_df, sequence_json = parse_bpmn_rename_unnamed_gateways(bpmn_path)

        print("✅ BPMN 文件已保存并解析成功")
        return bpmn,sequence_df, sequence_json
    except Exception as e:
        print(f"❌ 保存或解析 BPMN 失败: {e}")
        return None, None,None

def parse_bpmn_rename_unnamed_gateways(file_path):
    tree = ET.parse(file_path)
    root = tree.getroot()
    process = root.find('bpmn:process', NS)
    if process is None:
        raise ValueError("未找到 bpmn:process 节点")

    id_name_map = {}
    outgoing_map = defaultdict(list)
    incoming_map = defaultdict(list)

    # Step 1: 记录所有非 sequenceFlow 节点的名称（缺省为 "Unnamed"）
    for elem in process:
        tag = elem.tag.split('}')[-1]
        if tag != 'sequenceFlow':
            node_id = elem.attrib.get('id')
            if not node_id:
                continue
            name = (elem.attrib.get('name') or '').strip()
            id_name_map[node_id] = name if name else "Unnamed"

    # Step 2: 建图（source -> target）
    for seq in process.findall('bpmn:sequenceFlow', NS):
        source = seq.attrib.get('sourceRef')
        target = seq.attrib.get('targetRef')
        if not source or not target:
            continue
        outgoing_map[source].append(target)
        incoming_map[target].append(source)

    # Step 3: 找 startEvent
    start_node_id = None
    for elem in process.findall('bpmn:startEvent', NS):
        start_node_id = elem.attrib.get('id')
        if start_node_id:
            break
    if not start_node_id:
        raise ValueError("未找到 startEvent 节点")

    # Step 4: BFS 遍历，生成路径
    path_records = []
    queue = deque([start_node_id])
    seq_id = 1
    visited = set()

    while queue:
        current_id = queue.popleft()
        current_name = id_name_map.get(current_id, 'Unnamed')

        for next_id in outgoing_map.get(current_id, []):
            next_name = id_name_map.get(next_id, 'Unnamed')

            path_records.append({
                'id': seq_id,
                'now_state': current_name,
                'next_state': next_name
            })
            seq_id += 1

            if next_id not in visited:
                visited.add(next_id)
                queue.append(next_id)

    # 构建 DataFrame 和 JSON
    sequence_df = pd.DataFrame(path_records, columns=['id', 'now_state', 'next_state'])
    sequence_json = sequence_df.to_dict(orient='records')
    return sequence_df, sequence_json



# sequence_df,sequence_json=parse_bpmn_rename_unnamed_gateways(r"C:\Users\renming\Desktop\研究生\农业部项目\ProMoAI-main\bpmn\generated_model_20250902_143109_66b85f.bpmn")
# print(sequence_json)



